﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Browser;
using System.Windows.Browser;
using RestaurantBooker.Model;
using RestaurantBooker.Web.Services;
using RestaurantBooker.Web.Data;
using RestaurantBooker.ViewModel;
using System.Net.NetworkInformation;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace RestaurantBooker.ServiceProcessor
{
    public class ServiceProcessor : BasePropertyChangedObject
    {
        #region [ Singleton ]
        private static object _padlock = new object();

        static ServiceProcessor _instance;

        public static ServiceProcessor Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_padlock)
                    {
                        if (_instance == null)
                        {
                            // using client stack
                            HttpWebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
                            HttpWebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);
                            // create processor instance
                            _instance = new ServiceProcessor();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Private constructor
        /// </summary>
        private ServiceProcessor()
        {
            /// add instance to Application resources for using from views
            Application.Current.Resources.Add("ServiceProcessorInstance", this);

        }
        #endregion

        /// <summary>
        /// Initialization - must exist - useed for initialization Instance of the ServerProcessor class
        /// </summary>
        public void Initialization()
        { }


        #region DomainService initialization and finalization
        private RestaurantBookerDomainContext _doaminContext;


        private static String GetServiceAddress(String host)
        {
            // get service address
            String addr = null;

            String serviceRef = @"ClientBin/RestaurantBooker-Web-Services-RestaurantBookerDomainService.svc";

            if (HtmlPage.IsEnabled && host == null) // Application was executed in a browser or host is not initialized
            {
                if (String.IsNullOrEmpty(HtmlPage.Document.DocumentUri.AbsolutePath))
                    addr = HtmlPage.Document.DocumentUri.AbsoluteUri + serviceRef;
                else if (HtmlPage.Document.DocumentUri.AbsolutePath == "/")
                    addr = HtmlPage.Document.DocumentUri.AbsoluteUri + serviceRef;
                else
                {
                    addr = HtmlPage.Document.DocumentUri.AbsoluteUri;
                    addr = addr.Remove(addr.Length - HtmlPage.Document.DocumentUri.AbsolutePath.Length, HtmlPage.Document.DocumentUri.AbsolutePath.Length) + @"/" + serviceRef;
                }
#if DEBUG
                //set special URL for DEBUG
                // for looking the trafic in fidler - add dot after localhost
                addr = addr.Replace("localhost", "localhost.");
#endif
            }
            else // .. in the OOB mode or host is initialized
            {
#if DEBUG
                //set special URL for DEBUG                
#else
                addr = "https://" + host +"/"+serviceRef; 
#endif
            }
            return addr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="host"> host where is service!!</param>
        private bool CreateDomainContext(string host = null)
        {
            if (_doaminContext == null)
            {
                var addr = GetServiceAddress(host); //DONE: host parameter will be use            
                // create domain context instance
                _doaminContext = new RestaurantBookerDomainContext(new Uri(addr));


                //_doaminContext = new RestaurantBookerDomainContext();
                return true;
            }

            return false;
        }

        private void DestroyDomainContext()
        {
            IsLogined = false;
            _doaminContext = null;
        }

        #endregion

        #region Private members

        #endregion

        #region Properties

        ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();

        /// <summary>
        /// List of the customers
        /// </summary>
        public ObservableCollection<Customer> Customers
        {

            get { return _customers; }
        }

        String _userServerName = "";
        /// <summary>
        /// Property that is diplayed in MainPage - as user name which logged In
        /// </summary>
        public String UserServerName
        {
            get { return _userServerName; }
            set
            {
                if (_userServerName != value)
                {
                    _userServerName = value;
                    RaisePropertyChanged("UserServerName");
                }
            }
        }


        Booker _currentBooker = null;
        public Booker CurrentBooker
        {
            get { return _currentBooker; }
            set
            {
                if (_currentBooker != value)
                {
                    _currentBooker = value;
                    if (_currentBooker != null)
                        UserServerName = _currentBooker.booker_name;
                    RaisePropertyChanged("CurrentBooker");
                }
            }
        }

        bool _isLogined = false;
        public bool IsLogined
        {
            get { return _isLogined; }
            set
            {
                if (_isLogined != value)
                {
                    _isLogined = value;
                    RaisePropertyChanged("IsLogined");
                }
            }
        }

        bool _isLoginInProcess = false;

        public bool IsLoginInProcess
        {
            get { return _isLoginInProcess; }
            set
            {
                if (_isLoginInProcess != value)
                {
                    _isLoginInProcess = value;
                    RaisePropertyChanged("IsLoginInProcess");
                }
            }
        }


        private UserCredentials _userCredentials = new UserCredentials();
        /// <summary>
        /// User credentials used for logging in
        /// </summary>
        public UserCredentials UserCredentials
        {
            get { return _userCredentials; }
        }
        #endregion

        #region Services methods

        // !!!!!!!!!!!!!!!!!!!!
        // Simple login is implemented as temporary aproach
        // !!!!!!!!!!!!!!!!!!!!!


        public bool LoginBooker(LoginPageViewModel loginViewModel, UserCredentials userCredentials = null, string host = null)
        {
            if (userCredentials == null)
                userCredentials = _userCredentials;
            if (!IsLogined || !IsLoginInProcess)
            {

                if (NetworkInterface.GetIsNetworkAvailable() == false)
                {
                    loginViewModel.ErrorMessage = "Network is not available";
                    return false;
                }
                if (CreateDomainContext(host))
                {
                    EntityQuery<Booker> bookersQuery =
                        _doaminContext.GetBookersWithRestaurantAndRoomsQuery()
                        .Where(b => b.booker_login == userCredentials.UserName && b.booker_password == userCredentials.Password);

                    //initialize values
                    IsLoginInProcess = true;
                    CurrentBooker = null;
                    loginViewModel.ErrorMessage = null;

                    _doaminContext.Load(bookersQuery, (lo) =>
                    {
                        try
                        {
                            if (lo.Error != null)
                            {
                                //TODO: log error message
                                // have error                                
                                loginViewModel.ErrorMessage = "Server error: " + Environment.NewLine
                                    + lo.Error.ToString();
                                DestroyDomainContext();
                                lo.MarkErrorAsHandled();                             
                                return;
                            }
                            if (lo.Entities.Count() == 1)
                            {
                                CurrentBooker = lo.Entities.First();
                                IsLogined = true;
                                loginViewModel.LoginComplete(true);
                                LoadInitialalData();
                            }
                            else
                            {
                                loginViewModel.ErrorMessage = "Incorrect login or password";
                            }
                        }
                        finally
                        {
                            IsLoginInProcess = false;
                            if (!IsLogined)
                                DestroyDomainContext();
                        }

                    }, null);
                    return true;
                }
            }
            return false;
        }

        private void DesposeInitialalData()
        {
            this.Customers.Clear();
            UserServerName = null;
            CurrentBooker = null;
        }

        private void LoadInitialalData()
        {
            // load Customers
            LoadCustomers();
        }

        private void LoadCustomers()
        {
            var customerQuery = _doaminContext.GetCustomersQuery();
            _doaminContext.Load(customerQuery, (lo) =>
                {
                    if (lo.Error != null)
                    {
                        //TODO: add log
                        throw lo.Error;
                    }
                    else
                    {
                        Customers.AddRange(lo.Entities.ToList());
                    }

                }, false);
        }




        internal void LogoutBooker()
        {
            if (IsLogined)
            {
                //TODO: need service call
                DesposeInitialalData();
                DestroyDomainContext();

            }
        }

        /// <summary>
        /// Return set of Tabele_Room  include Table places by room_id
        /// </summary>
        /// <param name="room_Id"></param>
        /// <returns>set of </returns>
        internal void GetRoomTables(int room_Id, Action<List<Room_Table>> loadCallback)
        {
            EntityQuery<Room_Table> room_TablesQuery =
                 _doaminContext.GetRoom_TablesQueryWithTabelPlasesQuery()
                 .Where(b => b.room_id == room_Id);

            _doaminContext.Load(room_TablesQuery, (lo) =>
            {
                if (lo.Error != null)
                {
                    //TODO: Need corect processing for exception
                    throw lo.Error;
                }
                var tempAction = loadCallback;
                if (tempAction != null)
                    tempAction(lo.Entities.ToList());
            }, null);
        }

        /// <summary>
        /// Add new customer
        /// </summary>
        /// <param name="?"></param>
        /// <param name="?"></param>
        public void AddCustomer(Customer newCustomer, Action<Exception> callback)
        {            
            _doaminContext.Customers.Add(newCustomer);            
            _doaminContext.SubmitChanges((os) =>
                {
                    if (os.Error == null && os.IsComplete)
                    {// add customer to local cache
                        Customers.Add(newCustomer);                    
                    }

                    var tmpAction = callback;
                    if (tmpAction != null)
                        tmpAction(os.Error);
                }, null);
        }


        internal void AddNewOrder(Order newOrder, Action<Exception> callback)
        {
            _doaminContext.Orders.Add(newOrder);
            _doaminContext.SubmitChanges((os) =>
            {                
                var tmpAction = callback;
                if (tmpAction != null)
                    tmpAction(os.Error);
            }, null);
        }


        internal void  GetOrderForPeriod(int roomId, DateTime fromDateTime, 
            DateTime toDateTime, Action<Exception, IEnumerable<Order>> callback)
        {
            var query = _doaminContext.GetOrderForPeriodAndRoomQuery(roomId, fromDateTime, toDateTime);
 
            _doaminContext.Load(query, 
                (lo)=>
                {
                    if (lo.HasError)
                        lo.MarkErrorAsHandled();
                    var tmpAction = callback;
                    if (tmpAction != null)
                        tmpAction(lo.Error, lo.Entities);
                }, false);

                
            
        }
        #endregion


    }
}